#include "Shrink.h"
#include "Tips.h"

bool checkConnection( bool a[8] )
{
	bool flag;
	flag = ( !a[0] | !a[2] | a[1] | (a[3]&a[4]&a[5]&a[6]&a[7])  ) &
		( !a[2] | !a[4] | a[3] | (a[5]&a[6]&a[7]&a[0]&a[1])  ) &
		( !a[4] | !a[6] | a[5] | (a[7]&a[0]&a[1]&a[2]&a[3])  ) &
		( !a[6] | !a[0] | a[7] | (a[1]&a[2]&a[3]&a[4]&a[5])  ) &
		( !a[2] | !a[6] | (a[0]&a[1]&a[7]) | (a[3]&a[4]&a[5])) &
		( !a[0] | !a[4] | (a[1]&a[2]&a[3]) | (a[5]&a[6]&a[7]));
	return flag;
}

void Shrink()
{
	//skinImage = cvCreateImage(cvGetSize(binaryImage), IPL_DEPTH_8U, 3);
	//#ifdef DEBUG_MODE
	//	cvNamedWindow("Debug",1);
	//	cvShowImage("Debug",binaryImage);
	//#endif
	int height = binaryImage->height;
	int width = binaryImage->width;
	bool* skinHash = new bool[height*width];
	memcpy(skinHash, edgeHash, sizeof(bool)*height*width);
	queue<point*> skinList;


	for (int i = 0; i < height; i++){
		for (int j = 0; j <width;j++){
			if (edgeHash[i*width+j]){
				skinList.push(new point(i,j));
			}
		}
	}

	for (int i = 0; i < height; i++){
		for (int j = 0; j < width; j++){
			uchar* rawPtr = (uchar*)binaryImage->imageData;
			uchar* skinPtr = (uchar*)skinImage->imageData;
			if (rawPtr[i*width+j] <= 120) {
				skinPtr[i*skinImage->widthStep+j*3] = 0;
				skinPtr[i*skinImage->widthStep+j*3+1] = 0;
				skinPtr[i*skinImage->widthStep+j*3+2] = 0;
			}
			else
			{
				skinPtr[i*skinImage->widthStep+j*3] = 255;
				skinPtr[i*skinImage->widthStep+j*3+1] = 255;
				skinPtr[i*skinImage->widthStep+j*3+2] = 255;
			}

			if (skinHash[i*width+j]){
				skinPtr[i*skinImage->widthStep+j*3] = 255;
				skinPtr[i*skinImage->widthStep+j*3+1] = 0;
				skinPtr[i*skinImage->widthStep+j*3+2] = 0;
			}
		}
	}
#ifdef DEBUG_MODE
	cvNamedWindow("Debug",1);
	cvShowImage("Debug",skinImage);
#endif
	bool adj[8];
	int x,y;
	int count = 0;
	while (!(skinList.empty())){

		point* top = skinList.front();
		skinList.pop();
		x = top->x;
		y = top->y;
		skinHash[x*width+y] = false;
		if(tipHash[x*width+y] ) {
			continue;
		}

		uchar* ptr = (uchar*)binaryImage->imageData;
		adj[0] = (ptr[(x-1)*width + y]     <= 120);
		adj[1] = (ptr[(x-1)*width + y + 1] <= 120);
		adj[2] = (ptr[(x)  *width + y + 1] <= 120);
		adj[3] = (ptr[(x+1)*width + y + 1] <= 120);
		adj[4] = (ptr[(x+1)*width + y]     <= 120);
		adj[5] = (ptr[(x+1)*width + y - 1] <= 120);
		adj[6] = (ptr[(x)  *width + y - 1] <= 120);
		adj[7] = (ptr[(x-1)*width + y - 1] <= 120);

		if ( checkConnection(adj) ){
			ptr[x*width+y] = 150;

			if( ptr[(x-1)*width+y] == 0 && (!skinHash[(x-1)*width+y])&&
				(!tipHash[(x-1)*width+y])){
					skinHash[(x-1)*width+y] = true;
					skinList.push(new point(x-1, y));
					*(skinImage->imageData + (x-1)*skinImage->widthStep+3*y) = 255;
					*(skinImage->imageData + (x-1)*skinImage->widthStep+3*y+1) = 0;
					*(skinImage->imageData + (x-1)*skinImage->widthStep+3*y+2) = 0;
			}

			if( ptr[(x+1)*width+y] == 0 && (!skinHash[(x+1)*width+y])&&
				(!tipHash[(x+1)*width+y])){
					skinHash[(x+1)*width+y] = true;
					skinList.push(new point(x+1, y));
					*(skinImage->imageData + (x+1)*skinImage->widthStep+3*y) = 255;
					*(skinImage->imageData + (x+1)*skinImage->widthStep+3*y+1) = 0;
					*(skinImage->imageData + (x+1)*skinImage->widthStep+3*y+2) = 0;
			}

			if( ptr[(x)*width+y-1] == 0 && (!skinHash[(x)*width+y-1])&&
				(!tipHash[(x)*width+y-1])){
					skinHash[(x)*width+y-1] = true;
					skinList.push(new point(x, y-1));
					*(skinImage->imageData + x*skinImage->widthStep+3*(y-1)) = 255;
					*(skinImage->imageData + x*skinImage->widthStep+3*(y-1)+1) = 0;
					*(skinImage->imageData + x*skinImage->widthStep+3*(y-1)+2) = 0;
			}

			if( ptr[(x)*width+y+1] == 0 && (!skinHash[(x)*width+y+1])&&
				(!tipHash[(x)*width+y+1])){
					skinHash[(x)*width+y+1] = true;
					skinList.push(new point(x, y+1));
					*(skinImage->imageData + x*skinImage->widthStep+3*(y+1)) = 255;
					*(skinImage->imageData + x*skinImage->widthStep+3*(y+1)+1) = 0;
					*(skinImage->imageData + x*skinImage->widthStep+3*(y+1)+2) = 0;
			}
			*(skinImage->imageData + x*skinImage->widthStep+3*y)= 160;
			*(skinImage->imageData + x*skinImage->widthStep+3*y+1)= 160;
			*(skinImage->imageData + x*skinImage->widthStep+3*y+2)= 160;
		}
#ifdef DEBUG_MODE
		if (count == 1000){

			cvWaitKey(1);
			count = 0;
		}	
		count++;
#endif

		delete top;
	}

	//fix the nodes with 3 bifurcations.

	for (int i = 0; i < height; i++){
		for (int j = 0; j < width; j++){
			uchar* skinPtr = (uchar*)skinImage->imageData;
			if (skinPtr[i*skinImage->widthStep+j*3]    ==0&&
				skinPtr[i*skinImage->widthStep+j*3+1]  ==0&&
				skinPtr[i*skinImage->widthStep+j*3+2]  ==0)
			skinPtr[i*skinImage->widthStep+j*3] = 255;
		}
	}
	//cvShowImage("Show", skinImage);

	cvSaveImage("Shrink.bmp",skinImage);
}

void doShrinking()
{
	cout<<"Searching for tips..."<<endl;
	searchEdgePoints();

	searchTipPoints();
	cout<<"Doing shrinkage..."<<endl;

	Shrink();
	cout<<"Shrinkage completed."<<endl;
}